home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
MONITOR
/
ANSI137.ARJ
/
ANSI.ASM
next >
Wrap
Assembly Source File
|
1991-04-23
|
107KB
|
1,967 lines
;------------------------------------------------------------------------;
; ANSI.COM - Replacement for the ANSI.SYS console device driver. ;
; Unlike ANSI.SYS which must be installed at boot time, ANSI.COM ;
; can be installed and uninstalled at anytime. Enhancements include ;
; a fast screen write and variable sized keyboard reassignment buffer. ;
; ;
; Update 3/2/89 - Fix for DOS function 0Bh, Check Standard Input Status ;
; and STDIN in ANSI_INT_21 handler. ;
; Leading zero inserted for Device Status Report for ;
; single digit cursor positions. ;
; INFORMATION typo 40 for 40H. ;
; WRITE_FAST modified to handle CR and LF instead of ;
; WRITE_CHAR. ;
; Update 3/7/89 - Fix for CLS in graphics mode. Version 1.2 ;
; ;
; Update 8/8/89 - STI added to INT 21 and 29 handler. Version 1.3 ;
;: ;
;: Update 01/01/90 - [KON | KOFF] and [PON | POFF] added Version 1.31 ;
;: (all changes marked with ;: - Gary Meeker) ;
;| Update 03/23/90 - /Q option added for no output when exectued 1.32 ;
;| (all changes marked with ;| - Gary Meeker) ;
;; Update 05/16/90 - removed Syntax from output unless needed 1.33 ;
;; added /T test for load. ;
;; (all changes marked with ;; - Gary Meeker) ;
;. Update 10/28/90 - Added /P nn top of screen protection mode 1.34 ;
;. (all changes marked with ;. - Gary Meeker) ;
;@ Update 12/01/90 - Added PCBoard @X## color code support 1.35 ;
;@ also added @Variable@ Support ;
;@ added /S to load stats data from file ;
;@ (all changes marked with ;@ - Gary Meeker) ;
;& ;
;& Update 12/12/90 - My mistake, I didn't realize @X00 saved 1.36 ;
;& the color and @XFF restored it. Works now. ;
;& Also made prompts return to start of line ;
;& when cleared (like PCBoard does) & QON ;
;& now changes @MORE@ & @PAUSE@ into @WAIT@ ;
;& (all changes marked with ;& - Gary Meeker) ;
;$ Update 4/22/91 - Change set cursor routine. If new position 1.37 ;
;$ is off the screen use the last row or column. ;
;$ Added switch to not include PCB code. ;
;$ Corrected syntax display. ;
;$ Modified CLS to honor protected (/P) lines. ;
;$ Modified /T to returned status switches. ;
;$ Modified /P to accept * to mean current row-1. ;
;$ (all changes marked with ;$ - Wayne Mingee) ;
; ;
; PC Magazine - Michael J. Mefford ;
;------------------------------------------------------------------------;
PCB equ 0 ;$ 1 = if PCB code wanted
_TEXT SEGMENT PUBLIC 'CODE'
ASSUME CS:_TEXT,DS:_TEXT,ES:_TEXT,SS:_TEXT
ORG 100H
START: JMP INITIALIZE
; DATA AREA
SIGNATURE DB CR,SPACE,SPACE,SPACE,CR,LF
COPYRIGHT DB "ANSI 1.3g (C) 1988 Ziff Communications Co.",CR,LF ;.
PROGRAMMER DB "PC Magazine ",BOX," Michael J. Mefford",CR,LF,LF,"$"
DB CTRL_Z
CR EQU 13
LF EQU 10
CTRL_Z EQU 26
SPACE EQU 32
BOX EQU 254
ESC_CHAR EQU 27
SINGLE_QUOTE EQU 39
DOUBLE_QUOTE EQU 34
BELL EQU 7
BS EQU 8
TAB EQU 9
OFF EQU 1
ON EQU 2
SLOW EQU 4
FAST EQU 8
KOFF EQU 16 ;:
KON EQU 32 ;:
POFF EQU 64 ;:
PON EQU 128 ;:
STATUS_MASK EQU 1111110000000001B ;: Set Mask and Bit
ANSI_STATE DW ESC_STATE
STATUS DB ON OR FAST OR KON OR PON ;:
PARAMETERS DB "OFF",0,"ON",0,0,"SLOWFASTKOFFKON",0,"POFFPON",0 ;:
LAST_PARAMETER EQU $ - PARAMETERS
OLD_INT_29 DW ?,?
OLD_INT_16 DW ?,?
OLD_INT_21 DW ?,?
if PCB ;$
OLD_INT_08 DW ?,? ;@ Save Timer Interupt
endif ;$
ATTRIBUTE DB 7
if PCB ;$
SAVE_ATTRIBUTE DB ? ;& For saving attribute
endif ;$
SAVE_POSITION DW 0
LINE_WRAP DB ON
QUOTE_TYPE DB ?
ESC_COUNT DW 0
NUMBER_COUNT DW 0
COMMAND_TABLE LABEL BYTE
DB "H", "A", "B", "C", "D", "f", "n", "s", "u", "K", "m", "h", "l", "p", "J"
COMMAND_LENGTH EQU $ - COMMAND_TABLE
DW CURS_POSITION, CURSOR_UP, CURSOR_DOWN, CURS_FORWARD, CURS_BACKWARD
DW HORZ_VERT_POS, DEVICE_STATUS, SAVE_CURSOR, RESTORE_CURS, ERASE_IN_LINE
DW SGR, SET_MODE, RESET_MODE, REASSIGNMENT, CLS
COMMAND_END EQU $ - 2
ATTRIBUTE_TABLE LABEL BYTE
DB 00,01,04,05,07,08,30,31,32,33,34,35,36,37,40,41,42,43,44,45,46,47
ATTRIBUTE_LENGTH EQU $ - ATTRIBUTE_TABLE
;Format: AND mask,OR mask
DB 000H,07H, 0FFH,08H, 0F8H,01H, 0FFH,80H, 0F8H,70H, 088H,00H
DB 0F8H,00H, 0F8H,04H, 0F8H,02H, 0F8H,06H, 0F8H,01H, 0F8H,05H
DB 0F8H,03H, 0F8H,07H, 08FH,00H, 08FH,40H, 08FH,20H, 08FH,60H
DB 08FH,10H, 08FH,50H, 08FH,30H, 08FH,70H
ATTRIBUTE_END EQU $ - 2
if PCB ;$
AT_VARIABLE_TABLE LABEL BYTE ;@
DB '@CLS@CLREOL@AUTOMORE@POFF@PON@QOFF@QON@BEEP@MORE@PAUSE@WAIT@HANGUP@' ;@
DB 'OPTEXT@FIRSTU@FIRST@USER@CITY@HOMEPHONE@DATAPHONE@PROLTR@PRODESC@' ;@
DB 'EXPDATE@LASTDATEON@LASTTIMEON@INCONF@CONFNAME@BOARDNAME@' ;@
DB 'LASTCALLERNODE@LASTCALLERSYSTEM@EVENT@SYSOPIN@SYSOPOUT@BPS@NODE@' ;@
DB 'SYSDATE@SYSTIME@FILERATIO@BYTERATIO@' ;@
DB 'SECURITY@NUMCALLS@NUMTIMESON@TIMELEFT@TIMELIMIT@TIMEUSED@TOTALTIME@' ;@
DB 'BYTESLEFT@BYTELIMIT@DLFILES@UPFILES@KBLEFT@KBLIMIT@CONFNUM@' ;@
DB 'CURMSGNUM@HIGHMSGNUM@LOWMSGNUM@MSGREAD@MSGLEFT@NUMBLT@NUMDIR@' ;@
DB 'DAYBYTES@MINLEFT@DLBYTES@UPBYTES@EXPDAYS@' ;@
LAST_VARIABLE EQU $ - 1 ;@
JUMP_TABLE LABEL WORD ;@
DW CLS, ERASE_IN_LINE, AUTOMORE, AUTOOFF, AUTOOFF, QOFF, QON ;&;@
DW BEEP, MORE, PAUSE, WAIT1, SKIP_IT ;@
SUB_TABLE LABEL BYTE ;@
DB 8,15,15,25,24,13,13,1,47 ;@
DB 8,8,5,33,13,63 ;@
DB 52,52,5,5,5,5,2 ;@
DB 0,0,4,4 ;@
DB 2,4,2,2,2,2,2 ;@
DB 4,4,2,2,4,4,2 ;@
DB 4,4,4,4,4,2,2 ;@
DB 4,2,4,4,2 ;@
SUB_LIST LABEL BYTE ;@
DB '!OPTEXT!' ;@ OPTTEXT
DB 'GARY',0,0,0,0,0,0,0,0,0,0,0 ;@ FirstU
DB 'Gary',0,0,0,0,0,0,0,0,0,0,0 ;@ First
DB 'GARY MEEKER',0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;@ User
DB 'LAWRENCEVILLE, GA',0,0,0,0,0,0,0 ;@ City
DB '404 995-2699',0 ;@ HomePhone
DB '404 962-1788',0 ;@ DataPhone
DB 'Z' ;@ Proltr
DB 'Zmodem (Batch U/L and D/L)', 15 DUP (0) ;@ ProDesc
DB '01-01-91' ;@ ExpDate
DB '12-01-90' ;@ LastDateOn
DB '08:00' ;@ LastTimeOn
DB 'Magazine (1) Conference ',0,0,0,0,0,0,0,0,0 ;@ InConf
DB 'Magazine',0,0,0,0,0 ;@ ConfName
DB 'SHARP Technical Support Line BBS', 31 DUP (0) ;@ BoardName
DB 'MIKE BATE (RIVERDALE, GA)', 27 DUP (0) ;@ LastCallerNode
DB 'GARY MEEKER (LAWRENCEVILLE, GA)', 21 DUP (0) ;@ LastCallerSystem
DB '04:30' ;@ Event
DB '08:30' ;@ SysopIn
DB '17:00' ;@ SysopOut
DB '2400',0 ;@ bps
DB '1',0 ;@ Node
DD 48 ;@ FileRatio
DD 128 ;@ ByteRatio
DW 120 ;@ Security
DD 23946 ;@ numcalls
DW 2107 ;@ numtimeson
DW 61 ;@ timeleft
DW 121 ;@ timelimit
DW 59 ;@ timeused
DW 59 ;@ totaltime
DD 9508313 ;@ bytesleft
DD 10238976 ;@ bytelimit
DW 44 ;@ dlfiles
DW 1234 ;@ upfiles
DD 9285 ;@ KBLeft
DD 9999 ;@ KBLimit
DW 1 ;@ ConfNum
DD 4734 ;@ CurMsgNum
DD 4745 ;@ HighMsgNum
DD 2 ;@ LowMsgNum
DD 3456 ;@ MsgRead
DD 987 ;@ MsgLeft
DW 20 ;@ NumBlt
DW 31 ;@ NumDir
DD 30663 ;@ DayBytes
DW 61 ;@ MinLeft
DD 730663 ;@ DLBytes
DD 12345678 ;@ UpBytes
DW 0 ;@ ExpDays
SUB_LENGTH EQU $ - SUB_LIST
PAUSE_TIMER DW 182 ;@
PAUSE_DELAY DW 182 ;@ 10 Seconds
MORE_DELAY DW 182 * 12 ;@ 2 Minute
WAIT_DELAY DW 182 * 12 ;@ 2 Minute
WAIT_PROMPT DB 'press enter to continue' ;@
WAIT_LENGTH DW $ - WAIT_PROMPT ;@
MORE_PROMPT DB '(61 min left), (H)elp, More?' ;@
MORE_LENGTH DW $ - MORE_PROMPT ;@
COMMA DB 0 ;@
Q_STATE DB 0 ;&
endif ;$
BIOS_ACTIVE_PAGE EQU 62H
ACTIVE_PAGE DB ?
ADDR_6845 DW ?
BIOS_CRT_MODE EQU 49H
CRT_MODE DB ?
CRT_COLS DW ?
CRT_LEN DW ?
CRT_START DW ?
CRT_DATA_LENGTH EQU $ - CRT_MODE
CURSOR_POSN LABEL WORD
CURSOR_COL DB ?
CURSOR_ROW DB ?
CRT_ROWS DB ?
START_SCROLL DB 0 ;. Number of rows to protect from scrolling up
DOS_INPUT DB OFF
BUSY_FLAG DB OFF
REASSIGN_FLAG DB OFF
REMOVE_FLAG DB ON
REASSIGN_COUNT DW 0
REASSIGN_POS DW ?
FUNCTION_16 DB ?
ZR EQU 1000000B
ESC_BUFFER_SIZE EQU 126
REASSIGNMENT_DEFAULT EQU 200
REASSIGNMENT_SIZE DW REASSIGNMENT_DEFAULT
REASSIGNMENT_MAX EQU 60 * 1024
REASSIGN_END DW REASSIGNMENT_BUFFER
BUFFER_END DW REASSIGNMENT_BUFFER + REASSIGNMENT_DEFAULT
; CODE AREA
;************* INTERRUPT HANDLERS *************;
;------------------------------------------------------------------------------;
; INT 29 is an undocumented interrupt called by DOS for output to the console. ;
;------------------------------------------------------------------------------;
ANSI_INT_29 PROC FAR
STI
PUSH AX ;Save all registers.
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH DS
PUSH ES
PUSH BP
CLD ;All string operations forward.
MOV BX,CS ;Point to our data segment.
MOV DS,BX
MOV ES,BX
CALL ANSI_STATE ;Call the current state of
; the ANSI Esc sequence.
POP BP
POP ES
POP DS
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX ;Restore all registers and
IRET ; and return
ANSI_INT_29 ENDP
;------------------------------------------------;
ANSI_INT_21 PROC FAR
PUSHF
CMP AH,0BH ;If DOS function 0Bh (Check
JNZ CK_INPUT ; Standard Input Status)
CMP CS:REASSIGN_FLAG,ON ; and reassignment in progress,
JNZ CK_INPUT ; including a Device Status
CALL DWORD PTR CS:OLD_INT_21 ; Request, then process call in
MOV AL,0FFH ; case control break pressed;
IRET ; then return 0FFh for char ready
CK_INPUT: CMP AH,3FH ;Read from STDIN?
JNZ CK_CONSOLE
OR BX,BX
JZ INPUT
CK_CONSOLE: CMP AH,0AH ;If DOS Int 21 functions Ah,
JZ INPUT ; 7h, 1h or console input (6h)
CMP AH,7 ; then tell INT 16, DOS input
JZ INPUT ; is active.
CMP AH,1
JZ INPUT
CMP AH,8
JZ INPUT
CMP AH,6
JNZ QUICK21_EXIT
CMP DL,0FFH
JZ INPUT
QUICK21_EXIT: POPF ;If not, let the original
JMP DWORD PTR CS:OLD_INT_21 ; interrupt process the call.
INPUT: POPF
MOV CS:DOS_INPUT,ON ;INT 16 handler flag.
PUSHF
CALL DWORD PTR CS:OLD_INT_21 ;Emulate an interrupt.
MOV CS:DOS_INPUT,OFF ;Turn flag back off.
STI
RET 2 ;Return with current flags.
ANSI_INT_21 ENDP
;-----------------------------------------------------------------;
; If we get here via a DOS console input call, any awaiting key ;
; in keyboard buffer is checked to see if it is to be reassigned. :
;-----------------------------------------------------------------;
ANSI_INT_16 PROC FAR
STI ;Interrupts back on.
PUSHF ;Preserve flags on stack.
PUSH BP
PUSH DS
PUSH AX
PUSH CS ;Point to our data.
POP DS
CMP DOS_INPUT,OFF ;If not called via a DOS
JZ QUICK16_EXIT ; console input call, exit.
CMP BUSY_FLAG,ON ;If already processing a call,
JZ QUICK16_EXIT ; exit.
CLD ;All string operations forward.
MOV BP,SP ;Base reference to flags on stack
MOV FUNCTION_16,AH ;Save function request.
AND AH,NOT 10H ;Strip possible extended request.
JZ GET_KEY ;If zero, it's wait for a key.
DEC AH ;Else, decrement. If zero it's
JZ KEY_STATUS ; key status, else exit.
QUICK16_EXIT: POP AX ;Restore registers and let
POP DS ; original interrupt handler
POP BP ; process the call.
POPF
JMP DWORD PTR CS:OLD_INT_16
KEY_STATUS: OR BYTE PTR [BP+6],ZR ;Assume none ready; ZR = 1.
GET_KEY: MOV BUSY_FLAG,ON ;Non-reentrant; flag busy.
POP AX
PUSH BX ;Save some more registers.
PUSH CX
PUSH DX
PUSH SI
PUSH DI
CMP REASSIGN_FLAG,ON ;Already in process of reassign?
JZ CK_BUFFER ;If yes, check kbd buffer.
PUSHF ;Else, emulate an interrupt.
CALL DWORD PTR OLD_INT_16
PUSHF ;Status call results in flags.
TEST FUNCTION_16,1 ;Was it a status call?
JZ CK_DUP ;If no, check key returned.
POPF ;Else, retrieve status results.
JZ INT16_EXIT ;If zero, no key waiting.
AND BYTE PTR [BP+6],NOT ZR ;Else, ZR = 0.
PUSHF ;PUSHF just to keep stack right.
CK_DUP: POPF ;Fix stack.
TEST STATUS,KOFF ;:If KOFF then we don't do
JNZ INT16_EXIT ;:re-assignments
MOV BX,AX ;Match procedure wants key in BX.
CALL CK_MATCH ;Check reassignment buffer.
JC INT16_EXIT ;If no match, exit.
MOV AX,[DI] ;Else, retrieve string length.
SUB AX,3 ;Adjust for length word and match
ADD DI,3 ; byte; same for string pointer.
OR BL,BL ;Extended key? ie. key = 0.
JNZ STORE_COUNT ;If no, save length and pointer.
DEC AX ;Else, adjust for extended code.
INC DI
STORE_COUNT: MOV REASSIGN_COUNT,AX ;Save string length.
MOV REASSIGN_POS,DI ;Save pointer to string.
MOV REASSIGN_FLAG,ON ;Flag that replacement in effect.
CK_BUFFER: TEST FUNCTION_16,1 ;Was it a key wait function call?
JNZ RETRIEVE ;If no, key status; get it.
CMP REMOVE_FLAG,OFF ;Removed it from kbd buffer?
JZ RETRIEVE ;If yes, get reassignment.
MOV AH,FUNCTION_16 ;Else, eat the replaced character
INT 16H ; via INT 16.
MOV REMOVE_FLAG,OFF ;Flag character eaten.
RETRIEVE: MOV DI,REASSIGN_POS ;Retrieve pointer to string.
MOV AX,[DI] ;Retrieve replacement character.
TEST FUNCTION_16,1 ;Is it a key wait function call?
JZ REMOVE ;If yes, bump pointer up one.
AND BYTE PTR [BP+6],NOT ZR ;If no, status call; ZR = 0
JMP SHORT INT16_EXIT ;All done.
REMOVE: INC REASSIGN_POS ;Move pointer to next character.
DEC REASSIGN_COUNT ;One less character to replace.
JZ REASSIGN_DONE ;If end of string, reset flags.
OR AL,AL ;Else, extended replacement?
JNZ INT16_EXIT ;If no, done here.
INC REASSIGN_POS ;Else adjust pointers.
DEC REASSIGN_COUNT
JNZ INT16_EXIT ;End of string?
REASSIGN_DONE: MOV REASSIGN_FLAG,OFF ;If yes, reset flags.
MOV REMOVE_FLAG,ON
INT16_EXIT: MOV BUSY_FLAG,OFF ;Reset the busy flag.
POP DI ;Restore registers.
POP SI
POP DX
POP CX
POP BX
POP DS
POP BP
POPF ;Flags on stack have status
RET 2 ; results; kill old flags.
ANSI_INT_16 ENDP
if PCB ;$
ANSI_INT_08 PROC FAR ;@
CMP CS:[PAUSE_TIMER],0 ;@
JE SKIP_TIMER ;@
DEC CS:[PAUSE_TIMER] ;@
SKIP_TIMER: JMP DWORD PTR CS:OLD_INT_08 ;@
ANSI_INT_08 ENDP ;@
endif ;$
;************* ANSI ESCAPE STATE ROUTINES ************* ;
ESC_STATE: MOV BX,OFFSET BRACKET_STATE ;Assume Esc character.
CMP AL,ESC_CHAR ;Is it Esc? If yes, store
JZ SHORT_JUMP ; char. and next state.
if PCB ;$
CMP DOS_INPUT,ON ;@ Called via DOS Input?
JE WRITE_CHAR2 ;@ Yes, Nevermind
MOV BX,OFFSET ATX_STATE ;@ No, Assume @ Character
CMP AL,"@" ;@ Is it @? If yes, store
JZ SHORT_JUMP ;@ char. and next state.
WRITE_CHAR2:
endif ;$
JMP WRITE_CHAR ;@ Else, normal char; write it.
;------------------------------------------------;
BRACKET_STATE: MOV BX,OFFSET SPECIAL_STATE ;Assume left bracket.
CMP AL,"[" ;Is it left bracket? If yes,
SHORT_JUMP: JZ STORE_STATE ;store char. and next state.
JMP FLUSH_BUFFER ;Else, flush Esc out of buffer.
;---------------------------------------------------------------;
; Must process <ESC>[2J (CLS) regardless if ANSI.COM ON or OFF. ;
;---------------------------------------------------------------;
SPECIAL_STATE: MOV BX,OFFSET CLS_STATE ;Assume Erase in Display code.
CMP AL,"2" ;Is it the "2" ?
JZ STORE_STATE ;If yes, progress to next state.
TEST STATUS,OFF ;Else, is ANSI OFF ?
JNZ FLUSH_BUFFER ;If yes, flush Esc, bracket.
MOV BX,OFFSET PARAM_STATE ;Else, check for first Set,
CMP AL,"=" ; Reset Mode characters of
JZ STORE_STATE ; "=" and "?".
CMP AL,"?"
JZ STORE_STATE ;If found, store.
JMP SHORT DO_PARAMETER ;Else, check other codes.
;------------------------------------------------;
CLS_STATE: CMP AL,"J" ;Is it second character of CLS?
MOV DI,OFFSET COMMAND_END ;If yes, execute
JZ EXECUTE ; Erase in Display procedure.
TEST STATUS,OFF ;ANSI OFF? If yes, flush buffer.
JNZ FLUSH_BUFFER ; If not fall through to process.
MOV BYTE PTR NUMBER_BUFFER,2 ;Store the previous 2.
DO_PARAMETER: MOV ANSI_STATE,OFFSET PARAM_STATE ;Parameter state.
;------------------------------------------------;
PARAM_STATE: CALL CK_QUOTE ;Is it single or double quotes?
JZ STORE_STATE ;If yes, store string state.
CMP AL,";" ;Is it semi-colon delimiter?
JNZ CK_NUMBER ;If no, check if number.
INC NUMBER_COUNT ;Else, increment number count.
JMP SHORT BUFFER_CHAR ;Buffer the semi-colon.
CK_NUMBER: CMP AL,"0" ;Is it below 0 ?
JB FLUSH_BUFFER ;If yes, illegal; flush buffer.
CMP AL,"9" ;Else, is it above 9 ?
JA DO_COMMAND ;If yes, check for command char.
CALL ACCUMULATE ;Else it's a number; accumulate.
JMP SHORT BUFFER_CHAR ;Buffer the character.
DO_COMMAND: MOV DI,OFFSET COMMAND_TABLE ;Point to legal ANSI commands.
MOV CX,COMMAND_LENGTH ;Number of commands.
REPNZ SCASB ;Check for a match.
JNZ FLUSH_BUFFER ;If no match, flush sequence.
INC NUMBER_COUNT ;Else, increment for last number.
MOV DI,OFFSET COMMAND_END ;Point to appropriate command
SHL CX,1 ; processing procedure.
SUB DI,CX
EXECUTE: CALL GET_BIOS_DATA ;Get cursor position data.
CALL DS:[DI] ;Do command subroutine.
JMP SHORT FLUSH_END ;Clear buffer.
;------------------------------------------------;
QUOTE_STATE: CMP AL,QUOTE_TYPE ;Is it an ending string quote?
JNZ BUFFER_CHAR ;If no, buffer literal.
MOV BX,OFFSET PARAM_STATE ;Else, back to parameter parsing.
;------------------------------------------------;
STORE_STATE: MOV ANSI_STATE,BX ;Store the ANSI state.
;------------------------------------------------;
BUFFER_CHAR: MOV DI,ESC_COUNT ;Buffer the character in case
CMP DI,ESC_BUFFER_SIZE ; ending ANSI command is illegal.
JZ FLUSH_BUFFER ;If buffer full, flush.
ADD DI,OFFSET ESC_BUFFER ;Point to next buffer position.
STOSB ;Store the character.
INC ESC_COUNT ;Increment the sequence count.
RET
;------------------------------------------------;
FLUSH_BUFFER: ;$
if PCB ;$
CALL BUFFER_CHAR ;@ Buffer current character also!
FLUSH_BUFF2: ;@
else ;$
PUSH AX ;Save the current character.
endif ;$
MOV SI,OFFSET ESC_BUFFER ;Point to the sequence buffer.
MOV CX,ESC_COUNT ;Count of buffered characters.
NEXT_FLUSH: LODSB ;Retrieve one.
PUSH CX ;Save counter and pointer.
PUSH SI
CALL WRITE_CHAR ;Write character to screen.
POP SI ;Restore counter and pointer.
POP CX
LOOP NEXT_FLUSH ;Flush entire buffer.
ife PCB ;$
POP AX ;@Retrieve last character.
CALL WRITE_CHAR ;@Write it also.
endif ;$
FLUSH_END: MOV ESC_COUNT,0 ;Reset counter.
MOV ANSI_STATE,OFFSET ESC_STATE ;Back to Esc state.
MOV NUMBER_COUNT,0 ;Reset parameter counter.
PUSH CS ;Point to our data.
POP ES
MOV DI,OFFSET NUMBER_BUFFER ;Clear number buffer
MOV CX,ESC_BUFFER_SIZE / 2 ; to zeros.
XOR AX,AX
REP STOSW
RET
if PCB ;$
;------------------------------------------------;@
COLOR_STATE: CMP AL,"0" ;@ Is it below 0 ?
JB FLUSH_BUFFER ;@ If yes, illegal; flush buffer.
CMP AL,"9" ;@ Else, is it 0 - 9 ?
JBE DO_COLOR ;@ If yes, Make color.
CMP AL,'A' ;@ Is it below A ?
JB FLUSH_BUFFER ;@ If yes, illegal; flush buffer.
CMP AL,'F' ;@ Is it Above F ?
JA FLUSH_BUFFER ;@ If yes, illegal; flush buffer.
SUB AL,7 ;@ Else, adjust for Hex
DO_COLOR: CALL ACCUM_COLOR ;@ Accumulate Color.
INC NUMBER_COUNT ;@ Count the character
CMP NUMBER_COUNT,2 ;@ Already got two?
BUFFER_JUMP: JNE SHORT BUFFER_CHAR ;&;@ No, Buffer the character.
TEST STATUS,OFF ;@ Else, is ANSI OFF ?
JNZ FLUSH_BUFFER ;@ If yes, flush Esc, bracket.
INC CL ;& Is it @XFF restore code?
JNZ COLOR_2 ;& No, Check for save code
MOV CL,SAVE_ATTRIBUTE ;& Yes, Get back saved attribute
JMP SHORT COLOR_3 ;& and set it
COLOR_2: DEC CL ;& Is it @X00 save code
JNZ COLOR_3 ;& No, just set it then
MOV CL,ATTRIBUTE ;& Yes, Save the attribute
MOV SAVE_ATTRIBUTE,CL ;&
JMP FLUSH_END ;& and flush it
COLOR_3: MOV ATTRIBUTE,CL ;@ Set Attribute
JMP FLUSH_END ;@ Clear Buffer
;------------------------------------------------;@
VARIABLE_SUB: CMP AL,'@' ;@ Is it another @
JB SHORT_FLUSH ;@ No, and not valid, so flush
JNZ BUFFER_JUMP ;&;@ No, Buffer the Char (indirect)
MOV DX,OFFSET AT_VARIABLE_TABLE ;@ Point to Variable List
XOR BX,BX ;@ Position in Variable List
NEXT_VARI: MOV DI,DX ;@ Load DI
INC BX ;@ Count the Variable
MOV SI,OFFSET ESC_BUFFER ;@ Point to our Variable
MOV CX,OFFSET LAST_VARIABLE ;@ Point to End of list
SUB CX,DI ;@ Minus our current location
MOV AL,'@' ;@ We need to start on these
REPNE SCASB ;@ so find one
JCXZ SHORT_FLUSH ;@ End of List? Yes
MOV DX,DI ;@ No,Save Variable list pointer
DEC DI ;@ No, Back up to the @
MOV CX,ESC_COUNT ;@ Length of Variable
REP CMPSB ;@ See if this one matches?
JNZ NEXT_VARI ;@ Didn't match that one!
CMP [DI],AL ;@ Last Character has to be an @
JNZ NEXT_VARI ;@ Nope
DEC BX ;@ Back up 1
CMP BX,11 ;@ Is it an Action code
JA NOT_ACTION ;@ No
SHL BX,1 ;@ BX x 2 for proper offset
MOV DI,OFFSET JUMP_TABLE ;@ Point to command table
ADD DI,BX ;@ Adjust to the desired command
JMP EXECUTE ;@ Execute the command
NOT_ACTION: MOV SI, OFFSET SUB_TABLE ;@ Point to Sub Table
MOV AX,BX ;@ Save Variable Number
MOV CX,BX ;@ Copy Variable Number
XOR BX,BX ;@ Zero Offset
XOR DX,DX ;@ Zero out initial length
SUB CX,11 ;@ Adjust Variable Number
NEXT_OFFSET: ADD BX,DX ;@ Add last Length to Offset
MOV DL,[SI] ;@ Get Length
INC SI ;@ Bump the pointer
LOOP NEXT_OFFSET ;@ Do all of them
MOV CX,DX ;@ Get last Length
MOV SI,OFFSET SUB_LIST ;@ Point to the substitue buffer.
ADD SI,BX ;@ Add Offset
SUB AX,34 ;@ Is It a String Variable
JB IS_STRING ;@ Yes, Flush our Substitute
CMP AX,1 ;@ Is it Date ot Time?
JBE IS_TIMEDATE ;@ No, It's a Number
SUB AX,2 ;@ Adjust Variable number
CALL NUMBER_PARSE ;@ Create a Numeric value then
JMP SHORT VAR_END ;@ Flush our Number
IS_TIMEDATE: CALL MAKE_TIMEDATE ;@
IS_STRING: MOV DI,SI ;@ Copy Pointer to DI for SCASB
MOV AL,0 ;@ We look for a Zero
REPNE SCASB ;@ Findit it
JNE GOT_END ;@ Skip if we didn't hit a Zero
DEC DI ;@ Back up if we did
GOT_END: MOV CX,DI ;@ Get Current Location
SUB CX,SI ;@ Subtract Starting Location
VAR_END: JMP NEXT_FLUSH ;@ Flush our Substitute
SHORT_FLUSH: PUSH AX ;& Save Character
CALL FLUSH_BUFF2 ;@ Flush what we have now
POP AX ;& Restore Character
JMP ESC_STATE ;& And save the new state
;------------------------------------------------;@
ATX_STATE: MOV BX,OFFSET COLOR_STATE ;@ Assume 'X' Character
CMP AL,"X" ;@ Is it X? If yes, store
JZ SHORT_STORE ;@ char. and next state.
CMP AL,'@' ;@ Is it another '@' already?
JZ WRITE_CHAR ;@ Yes!
CMP AL,ESC_CHAR ;& Is it an Escape
JE SHORT_FLUSH ;& Yes, so flush buffer & restart
MOV BX,OFFSET VARIABLE_SUB ;@ No, Must be a Variable
SHORT_STORE: JMP STORE_STATE ;@ Store the state
endif ;$
;------------------------------------------------;
; Slow video writes are via BIOS Write TTY. ;
;------------------------------------------------;
WRITE_CHAR: CMP AL,BELL ;Let BIOS process BS and BELL.
JZ WRITE_TTY
CMP AL,BS
JZ WRITE_TTY
CALL GET_BIOS_DATA ;Get BIOS video data.
CMP AL,TAB ;Is character a TAB?
JNZ CK_ACTIVE ;If no, process normally.
MOV CX,CURSOR_POSN ;Else, expand TAB to
AND CX,7 ; appropriate space characters.
NEG CX
ADD CX,8
NEXT_TAB: PUSH CX
MOV AL,SPACE
CALL CK_ACTIVE
POP CX
LOOP NEXT_TAB
RET
CK_ACTIVE: TEST STATUS,OFF ;Is ANSI OFF?
JNZ WRITE_TTY ;If yes, write via BIOS TTY.
CK_FAST: CALL CK_SLOW_TEXT ;Is ANSI SLOW or in graphics
JNC WRITE_FAST ; mode? If no, write fast.
CMP AL,CR ;Let BIOS handle CR and LF.
JZ WRITE_TTY
CMP AL,LF
JZ WRITE_TTY
WRITE_SLOW: PUSH AX ;Else, write character/attribute
MOV CX,1 ; at current cursor position
MOV BH,ACTIVE_PAGE ; via BIOS.
MOV BL,ATTRIBUTE
MOV AH,9
INT 10H
POP AX
CMP LINE_WRAP,ON ;Is line wrap on?
JZ TTY ;If yes, continue.
MOV CX,CRT_COLS ;Else, cursor at rightmost
DEC CL ; column?
CMP CL,CURSOR_COL ;If yes, continue, else
JNZ TTY ; return without writing.
RET
;------------------------------------------------;
WRITE_TTY: MOV BL,7 ;Attribute in graphics mode.
TTY: MOV AH,0EH
INT 10H
RET
;----------------------------------------------------------------------------;
; Fast screen writes are directly to the video buffer without retrace check. ;
;----------------------------------------------------------------------------;
WRITE_FAST: PUSH ES ;Preserve extra segment.
MOV DX,CURSOR_POSN ;Retrieve cursor position.
CMP AL,CR ;Carriage return?
JNZ CK_LINEFEED ;If no, check linefeed.
XOR DL,DL ;Else, cursor to first column.
JMP SHORT UPDATE_CURSOR
CK_LINEFEED: CALL VIDEO_SETUP ;Calculate video address.
CMP AL,LF ;Linefeed?
JZ NEXT_ROW ;If yes, next row.
MOV AH,ATTRIBUTE ;Retrieve attribute.
STOSW ;Put char/attrib in video buffer.
INC DL ;Increment cursor column.
CMP DL,BYTE PTR CRT_COLS ;End of row?
JB UPDATE_CURSOR ;If no, update cursor.
CMP LINE_WRAP,OFF ;Else, line wrap off?
JZ FAST_END ;If yes, don't move cursor.
XOR DL,DL ;Else, column zero.
NEXT_ROW: INC DH ;Next row.
CALL INFORMATION ;Get displayable row info.
CMP DH,AL ;Beyond the bottom of screen?
JBE UPDATE_CURSOR ;If no, update cursor.
DEC DH ;Else, cursor to original row.
MOV AX,CRT_COLS ;. Get number of columns
PUSH DX ;. Save Cursor position
PUSH AX ;. Save Columns
SHL AX,1 ;. Twice for Char/Attribute
MOV SI,AX ;. Starting offset in SI
MOV DL,START_SCROLL ;. Where we allow scroll to start
MUL DL ;. Calculate offset in AX
SUB DH,DL ;. Reduce line count
MOV DI,CRT_START ;Point destination to top.
ADD DI,AX ;. Add offset
ADD SI,DI ;. Point source to second row
POP AX ;. Get Back Columns
PUSH AX ;. Save it again
MUL DH ;Times displayable rows - 1.
MOV CX,AX ; equals char/attrib to scroll.
PUSH DS ;Save data segment and
PUSH ES ; point to video segment.
POP DS
REP MOVSW ;Scroll the screen.
POP DS ;Restore data segment.
POP CX ;Retrieve CRT columns.
POP DX ;.Get back Cursor info
MOV AL,SPACE ;Write space/attrib to
MOV AH,ATTRIBUTE ; bottom row.
REP STOSW
UPDATE_CURSOR: CALL SET_CURSOR ;Update the cursor position.
FAST_END: POP ES ;Restore extra segment.
RET
if PCB ;$
;@************ SUPPORT ROUTINES *************;
QOFF: MOV AL,0FFH ;& Show QOFF
JMP SHORT QON2 ;&
QON: XOR AL,AL ;& Show QON
QON2: MOV Q_STATE,AL ;& Store the state
RET ;& All done
BEEP: MOV AL,7 ;@
JMP WRITE_CHAR ;@
MORE: MOV AX,MORE_DELAY ;@
JMP SHORT PAUSE2 ;@
PAUSE: MOV AX,PAUSE_DELAY ;@
PAUSE2: CMP Q_STATE,0 ;& Are we at a QOFF state
JNZ WAIT1 ;& Yes, then these are WAITs
MOV SI,OFFSET MORE_PROMPT ;@ Point to More? Prompt
MOV CX,MORE_LENGTH ;@ Get the Length of it
JMP SHORT WAIT2 ;@ Go Wait
WAIT1: MOV AX,WAIT_DELAY ;@
MOV SI,OFFSET WAIT_PROMPT ;@ Point to Wait Prompt
MOV CX,WAIT_LENGTH ;@ Get the Length of it
WAIT2: MOV PAUSE_TIMER,AX ;@
; PUSH CX ;@ Save Length
WAIT2_LOOP: LODSB ;@ Get Character
PUSH CX ;@
PUSH SI ;@
CALL WRITE_CHAR ;@ Output it
POP SI ;@
POP CX ;@
LOOP WAIT2_LOOP ;@ Do all the Characters
WAIT_LOOP: MOV AH,1 ;@ KeyBoard Status
INT 16H ;@ Keyboard I/O Services
JNZ CONTINUE ;@ Key Hit!
CMP PAUSE_TIMER,0 ;@ Timer Run out?
JNE WAIT_LOOP ;@ No
JMP SHORT WAIT3 ;@ Yes
CONTINUE: MOV AH,0 ;@ KeyBoard Read
INT 16H ;@ Keyboard I/O Services
WAIT3: ;@
XOR CL,CL ;@ Clear the Attribute
XCHG CL,ATTRIBUTE ;& and
PUSH CX ;& save it too
MOV DX,CURSOR_POSN ;& Get Cursor
MOV DL,0 ;& First column
CALL SET_CURSOR ;& Set the new Cursor
CALL ERASE_2 ;& Erase the line
;&
;& The following lines (and the PUSH CX above) will clear only the PROMPT
;& and leave the cursor where it was when the prompt appears. This was
;& different than PCBoard 14.5 so I changed it to clear the entire line
;& via the above four lines. Left the old code in case someome wanted it.
;&
;& POP CX ;@ Get back Length
;&ERASE_LOOP: PUSH CX ;@ Save it again
;& MOV AL,BS ;@ Send BS
;& CALL WRITE_CHAR ;@ character
;& MOV AL,' ' ;@ and then space
;& CALL WRITE_CHAR ;@ character to erase
;& MOV AL,BS ;@ then BS again
;& CALL WRITE_CHAR ;@ to backup.
;& POP CX ;@ get back length again
;& LOOP ERASE_LOOP ;@ Erase entire prompt.
POP CX ;& Restore
MOV ATTRIBUTE,CL ;& the Attribute
RET ;@
AUTOOFF: MOV AX,WAIT_DELAY ;@ Restore More to a Wait
JMP SHORT AUTO2 ;@
AUTOMORE: MOV AX,PAUSE_DELAY ;@ Make More a Pause
AUTO2: MOV MORE_DELAY,AX ;@
SKIP_IT: RET ;@
endif ;$
;************* SUPPORT ROUTINES *************;
CK_QUOTE: MOV BX,OFFSET QUOTE_STATE ;Assume quote state.
MOV AH,DOUBLE_QUOTE
CMP AL,AH ;Is it double quote?
JZ GOT_QUOTE ;If yes, string delimiter.
MOV AH,SINGLE_QUOTE ;Is it single quote?
CMP AL,AH ;Is yes, string delimiter.
JNZ QUOTE_END ;Else, return ZR = 0.
GOT_QUOTE: MOV QUOTE_TYPE,AH ;Store as matching string end.
QUOTE_END: RET
;------------------------------------------------;
ACCUMULATE: PUSH AX ;Preserve number character.
SUB AL,"0" ;Convert ASCII to binary.
MOV CL,AL ;Save the number.
MOV AX,10 ;Multiply previous count by 10.
MOV BX,NUMBER_COUNT
MUL BYTE PTR NUMBER_BUFFER[BX]
ADD AL,CL ;Add in new number
MOV BYTE PTR NUMBER_BUFFER[BX],AL ; and store.
POP AX
RET
if PCB ;$
;------------------------------------------------;@
ACCUM_COLOR: PUSH AX ;@ Preserve number character.
SUB AL,"0" ;@ Convert ASCII to hex
MOV AH,BYTE PTR NUMBER_BUFFER ;@ Get current number
SHL AH,1 ;@ Shift left 4 bits
SHL AH,1 ;@
SHL AH,1 ;@
SHL AH,1 ;@
OR AH,AL ;@ Add in new number
MOV BYTE PTR NUMBER_BUFFER,AH ;@ and store.
MOV CL,AH ;@ Return with Color in CL
POP AX ;@
RET ;@
NUMBER_PARSE: MOV BX,[SI] ;@ Get low word
MOV DX,AX ;@ Save Variable Number
XOR AX,AX ;@ Assume Integer
CMP CX,4 ;@ Is that a Long Integer?
JNE SHORT_INT ;@ No, Integer
MOV AX,[SI + 2] ;@ Yes, Get high word
SHORT_INT: MOV DI, OFFSET PARSE_BUFFER ;@ Point DI at number space
MOV CX,10 ;@ Set divisor to 10
XOR SI,SI ;@ Clear SI as counter
MOV COMMA,2 ;@ Assume a Ratio
CMP DX,2 ;@ Is it a Ratio?
JB GETDIGIT ;@ No
RESET_COMMA: MOV COMMA,4 ;@ Set Comma Counter
GETDIGIT: DEC DI ;@ Point DI at correct character
INC SI ;@ Register that we have a character
DEC COMMA ;@ Do we need a Comma?
JNZ NO_COMMA ;@ No
MOV BYTE PTR [DI],',' ;@ Yes, So Put one in!
JMP RESET_COMMA ;@ And restart the Comma count.
NO_COMMA: XOR DX,DX ;@ Clear DX to take remainder
DIV CX ;@ Divide AX first (High word)
MOV BP,AX ;@ Save quotient
MOV AX,BX ;@ Get low word
DIV CX ;@ DX had leftover from first divide
MOV BX,AX ;@ Save quotient
MOV AX,BP ;@ Put high word back
ADD DL,30h ;@ Make it an ASCII digit from remainder
MOV [DI],DL ;@ Put it in our string
OR AX,AX ;@ Is high word zero?
JNZ GETDIGIT ;@ No keep going
OR BX,BX ;@ Is low word zero?
JNZ GETDIGIT ;@ No keep going
MOV CX,SI ;@ Digit count to CX
MOV SI,DI ;@ String Pointer to SI
MOV DI, OFFSET PARSE_BUFFER ;@ Point DI at number space
CMP BYTE PTR [DI][-2],',' ;@ Was it a Ratio
JNE NOT_RATIO ;@ No, nevermind
MOV BYTE PTR [DI][-2],'.' ;@ Change comma to Decimal
MOV BYTE PTR [DI],":" ;@ Yes, Add ':1' to it
MOV BYTE PTR [DI][1],"1" ;@
ADD CX,2 ;@ Account for two more characers
NOT_RATIO: RET ;@
MAKE_TIMEDATE: MOV DI, OFFSET NUMBER_BUFFER ;@ Point to Number Buffer
MOV SI,DI ;@ Save it in SI too
JE MAKE_TIME ;@ It was TIME on Entry!
MOV CX,8 ;@ It was DATE! Length = 8
PUSH CX ;@ Save Length
MOV AH,04H ;@ Get date service
INT 1AH ;@ Call BIOS - return codes as follows:
PUSH CX ;@ CH = Century (19-20) CL = Year (00-99)
PUSH DX ;@ DH = Month (1-12) DL = Day (00-31)
MOV AL,DH ;@ Month
CALL ASCII ;@ Convert byte to ASCII digits
MOV AL,'-' ;@
STOSB ;@
POP AX ;@ Day - was DX when pushed
CALL ASCII ;@ Convert byte to ASCII digits
MOV AL,'-' ;@
STOSB ;@
POP AX ;@ Year - was CX when pushed
POP CX ;@ Restore Length
JMP SHORT ASCII ;@ Convert byte to ASCII digit
MAKE_TIME: MOV CX,5 ;@ Length = 5
PUSH CX ;@ Save Length
MOV AH,02H ;@ Get time service
INT 1AH ;@ Call BIOS - return codes as follows:
PUSH CX ;@ CH = Hours (0-23) CL = Minutes (0-59)
MOV AL,CH ;@ Hours
CALL ASCII ;@ Convert byte to ASCII digits
MOV AL,':' ;@
STOSB ;@
POP AX ;@ Minutes - was CX when pushed
POP CX ;@ Restore Length
ASCII: MOV AH,AL ;@ Need to get BCD Digits
SHR AL,1 ;@ MSD in AL
SHR AL,1 ;@ lower
SHR AL,1 ;@ 4
SHR AL,1 ;@ Bits
AND AH,0FH ;@ And LSD in AH cleanly
OR AX,3030H ;@ Convert to ASCII
STOSW ;@ Stuff Digits into Buffer (in reverse)
RET ;@ SI Points to String Data
endif ;$
;---------------------------------------------------------------------------;
; OUTPUT: CY = 1 if write SLOW mode or in graphics mode; CY = 0 otherwise. ;
;---------------------------------------------------------------------------;
CK_SLOW_TEXT: TEST STATUS,SLOW
JNZ SLOW_MODE
CMP CRT_MODE,7
JZ TEXT_MODE
CMP CRT_MODE,3
JA SLOW_MODE
TEXT_MODE: CLC
RET
SLOW_MODE: STC
RET
;-------------------------------------;
; OUTPUT: AL = Screen rows minus one ;
;-------------------------------------;
INFORMATION: PUSH DS ;Save data segment.
MOV AX,40H ;Point to BIOS data.
MOV DS,AX
MOV AL,DS:[84H] ;Retrieve rows - 1.
OR AL,AL ;BIOS supported?
JNZ INFO_END ;If yes, done here.
MOV AL,24 ;Else, assume 25 lines.
INFO_END: POP DS
RET
;------------------------------------------------------------------------------;
; INPUT: DX = Cursor position. ;
; OUTPUT: ES = Video buffer segment; DI = Video buffer offset; AX,DX preserved ;
;------------------------------------------------------------------------------;
VIDEO_SETUP: PUSH AX
MOV AX,CRT_COLS ;Retrieve CRT columns.
MUL DH ;Times cursor row.
MOV BL,DL
XOR BH,BH
ADD AX,BX ;Plus cursor column.
SHL AX,1 ;Times two for attribute.
MOV DI,CRT_START ;Plus starting video offset.
ADD DI,AX ;Equals destination.
MOV BX,0B000H ;Assume mono card.
CMP ADDR_6845,3B4H ;Is it mono port?
JZ VIDEO_SEGMENT ;If yes, guessed right.
ADD BX,800H ;Else, point to color segment.
VIDEO_SEGMENT: MOV ES,BX
POP AX
RET
;------------------------------------------------;
; Move BIOS video data into our data segment. ;
;------------------------------------------------;
GET_BIOS_DATA: PUSH DS
PUSH DI ;Point to BIOS data segment.
MOV BX,40H
MOV DS,BX
MOV SI,BIOS_ACTIVE_PAGE ;Start with active page.
MOV DI,OFFSET ACTIVE_PAGE
MOVSB ;Retrieve active page
MOVSW ; and address of 6845 port.
MOV SI,BIOS_CRT_MODE ;Retrieve CRT mode, CRT columns,
MOV CX,CRT_DATA_LENGTH ; CRT length, CRT start.
REP MOVSB
MOV BL,ES:ACTIVE_PAGE ;Use active page as index
XOR BH,BH ; of active cursor position.
SHL BX,1
ADD SI,BX
MOVSW
POP DI
POP DS
RET
;----------------------------------------------------------------------------;
; OUTPUT: BL = First parameter; BH = Second parameter; DX = cursor position. ;
;----------------------------------------------------------------------------;
ADJUST_NUMBER: MOV BX,WORD PTR NUMBER_BUFFER
OR BH,BH
JNZ ADJUST_AL ;If either parameter zero,
INC BH ; increment to convert
ADJUST_AL: OR BL,BL ; to base one.
JNZ ADJUST_END
INC BL
ADJUST_END: MOV DX,CURSOR_POSN ;Return cursor position.
RET
;--------------------------------------------------------------------------;
; INPUT: AX = number; BP = 0 if console output; BP = 1 if storage output. ;
;--------------------------------------------------------------------------;
DECIMAL_OUT: MOV BX,10 ;Divisor of ten.
XOR CX,CX ;Zero in counter.
NEXT_COUNT: XOR DX,DX ;Zero in high half.
DIV BX ;Divide by ten.
ADD DL,"0" ;Convert to ASCII.
PUSH DX ;Save results.
INC CX ;Also increment count.
CMP AX,0 ;Are we done?
JNZ NEXT_COUNT ;Continue until zero.
OR BP,BP ;If BP zero, output to screen.
JNZ DEVICE_OUTPUT ;Else, store in buffer.
REPORT_OUTPUT: POP AX ;Retrieve number.
CALL PRINT_CHAR ;Display it.
LOOP REPORT_OUTPUT
RET
DEVICE_OUTPUT: CMP CX,2 ;Two digits?
JZ TWO_DIGITS ;If yes, process normally.
MOV AL,"0" ;Else, store leading zero.
STOSB
TWO_DIGITS: POP AX ;Retrieve number.
STOSB ;Store it
LOOP TWO_DIGITS
RET
;************* ANSI COMMAND SUBSET *************;
CLS: MOV BH,7 ;Assume normal attribute.
MOV BL,BYTE PTR CRT_MODE ;Get current video mode.
CMP BL,4
JBE CK_CLS_STATUS ;If text mode then check if
CMP BL,7 ; ANSI active.
JZ CK_CLS_STATUS
XOR BH,BH ;Else, use black attribute
TEST STATUS,OFF
JNZ CLS_SLOW ;If ANSI inactive, via BIOS.
JMP SHORT CK_CLS_SLOW ;Else, check if FAST or SLOW.
CK_CLS_STATUS: TEST STATUS,OFF ;Is ANSI OFF?
JNZ CLS_SLOW ;If yes, CLS via BIOS.
MOV BH,ATTRIBUTE ;Else, current attribute.
CK_CLS_SLOW: CALL CK_SLOW_TEXT
JC CLS_SLOW ;If ANSI SLOW, via BIOS.
CLS_FAST: MOV AH,BH ;Else, attribute in high half.
MOV AL,SPACE ;Space character in low half.
XOR DX,DX ;Cursor position home.
MOV DH,START_SCROLL ;$ protect lines
CALL VIDEO_SETUP ;Calculate video address.
MOV CX,CRT_LEN ;Retrieve CRT length.
SHR CX,1 ;Times two for attribute.
REP STOSW ;Write directly to video buffer.
JMP SHORT SET_CURSOR ;Set the cursor top left corner.
CLS_SLOW: CALL INFORMATION ;Get displayable rows.
MOV DH,AL ;Store in DH.
MOV DL,BYTE PTR CRT_COLS ;Retrieve CRT columns.
DEC DL ;Adjust to zero base.
XOR CX,CX ;Scroll active page.
MOV AX,600H
INT 10H
XOR DX,DX ;Home the cursor.
JMP SHORT SET_CURSOR
;------------------------------------------------;
CURS_POSITION: ;These two commands are the same.
HORZ_VERT_POS: CALL INFORMATION ;Returns AL = screen rows.
CALL ADJUST_NUMBER ;Returns BL,BH = parameters.
SUB BX,101H ;Zero based.
CMP BL,AL ;If cursor request within
;$ JA CURSOR_END ; boundaries of screen, set it.
JBE ROWOK ;$boundaries of screen, set it.
MOV BL,AL ;$Set to last row
ROWOK: CMP BH,BYTE PTR CRT_COLS ;$
;$ JAE CURSOR_END
JB COLOK ;$
MOV BH,BYTE PTR CRT_COLS ;$Set to
DEC BH ;$ last column
COLOK: XCHG BH,BL ;
MOV DX,BX
SET_CURSOR: MOV BH,ACTIVE_PAGE ;Set cursor via BIOS.
MOV AH,2
INT 10H
CURSOR_END: RET
;------------------------------------------------;
CURSOR_UP: CALL ADJUST_NUMBER ;Move cursor up one or more rows
OR DH,DH ; without changing column.
JZ CURSOR_END ;If already at top, ignore.
SUB DH,BL
JNC SET_CURSOR ;Stay in bounds.
XOR DH,DH
JMP SHORT SET_CURSOR
;------------------------------------------------;
CURSOR_DOWN: CALL INFORMATION ;Returns AL = screen rows.
CALL ADJUST_NUMBER ;Returns BL,BH =nos.; DX = cursor
CMP DH,AL ;Move cursor down requested
JZ CURSOR_END ; rows without changing column.
ADD DH,BL
CMP DH,AL
JNA SET_CURSOR
MOV DH,AL ;Stay in bounds.
JMP SHORT SET_CURSOR
;------------------------------------------------;
CURS_FORWARD: CALL ADJUST_NUMBER ;Returns BL,BH =nos.; DX = cursor
MOV BH,BYTE PTR CRT_COLS ;Retrieve displayable columns.
DEC BH ;Adjust to zero base.
CMP DL,BH ;Move cursor forward one or more
JZ CURSOR_END ; columns without changing row.
ADD DL,BL
CMP DL,BH
JNA SET_CURSOR
MOV DL,BH ;Stay in bounds.
JMP SHORT SET_CURSOR
;------------------------------------------------;
CURS_BACKWARD: CALL ADJUST_NUMBER ;Returns BL,BH =nos.; DX = cursor
OR DL,DL ;Move cursor backward one or
JZ CURSOR_END ; more columns without changing
SUB DL,BL ; row. Ignore if already left.
JNC SET_CURSOR
XOR DL,DL ;Stay in bounds.
JMP SHORT SET_CURSOR
;--------------------------------------------------------;
; Report cursor position via console; Format: ESC[#;#R ;
;--------------------------------------------------------;
DEVICE_STATUS: MOV DI,OFFSET DEVICE_STATUS_BUFFER
MOV AL,ESC_CHAR
STOSB ;Store leading Esc, bracket
MOV AL,"[" ;in special Device Status Buffer.
STOSB
MOV AL,CURSOR_ROW ;Retrieve cursor row.
XOR AH,AH ;Zero in high half.
INC AX ;Convert to base one.
MOV BP,1 ;Flag as storage.
CALL DECIMAL_OUT ;Convert to ASCII.
MOV AL,";" ;Store delimiting semi-colon.
STOSB
MOV AL,CURSOR_COL ;Do same with cursor column.
XOR AH,AH
INC AX
CALL DECIMAL_OUT
MOV AL,"R" ;Add command character
STOSB
MOV AL,CR ; and carriage return.
STOSB
SUB DI,OFFSET DEVICE_STATUS_BUFFER ;Setup for console out.
MOV REASSIGN_COUNT,DI
MOV REASSIGN_POS,OFFSET DEVICE_STATUS_BUFFER
MOV REASSIGN_FLAG,ON
MOV REMOVE_FLAG,OFF
RET
;------------------------------------------------;
SAVE_CURSOR: MOV DX,CURSOR_POSN
MOV SAVE_POSITION,DX
RET
;------------------------------------------------;
RESTORE_CURS: MOV DX,SAVE_POSITION
JMP SET_CURSOR
;------------------------------------------------;
ERASE_IN_LINE: MOV DX,CURSOR_POSN ;Erase from the cursor to
ERASE_2: MOV CX,CRT_COLS ;& the end of the line, including
SUB CL,DL ; the current cursor position.
CALL CK_SLOW_TEXT
JC ERASE_SLOW ;If ANSI ON and not graphics,
CALL VIDEO_SETUP ; write directly to video buffer
MOV AL,SPACE ; with space/attribute.
MOV AH,ATTRIBUTE
REP STOSW
RET
ERASE_SLOW: MOV CX,DX ;Else, erase SLOW via
MOV DL,BYTE PTR CRT_COLS ; BIOS scroll active page.
DEC DL
MOV BH,ATTRIBUTE
MOV AX,601H
INT 10H
RET
;------------------------------------------------;
; Set Graphics Rendition ;
;------------------------------------------------;
SGR: MOV SI,OFFSET NUMBER_BUFFER ;Point to number parameters.
NEXT_SGR: LODSB ;Retrieve parameter.
MOV DI,OFFSET ATTRIBUTE_TABLE ;Look up attribute in
MOV CX,ATTRIBUTE_LENGTH ; translation table.
REPNZ SCASB
JNZ SGR_LOOP
MOV DI,OFFSET ATTRIBUTE_END
SHL CX,1
SUB DI,CX
MOV AX,[DI]
AND ATTRIBUTE,AL ;If match, AND with strip mask.
OR ATTRIBUTE,AH ;OR with add mask.
SGR_LOOP: DEC NUMBER_COUNT ;Do all parameters.
JNZ NEXT_SGR
RET
;------------------------------------------------;
SET_MODE: MOV AH,ON ;Assume command 7,
JMP SHORT CK_WRAP ; turn line wrap on.
;------------------------------------------------;
RESET_MODE: MOV AH,OFF ;Assume turn line wrap off.
CK_WRAP: MOV AL,BYTE PTR NUMBER_BUFFER ;Retrieve number parameter.
CMP AL,7 ;Is it 7?
JZ SET_WRAP ;If yes, set wrap mode.
JB DO_MODE ;1 - 6 valid modes.
CMP AL,14 ;14 - 16 valid modes.
JB MODE_END
CMP AL,19
JA MODE_END ;If above 16, illegal.
DO_MODE: XOR AH,AH ;Else, set video mode
INT 10H ; to parameter.
RET
SET_WRAP: MOV LINE_WRAP,AH
MODE_END: RET
;--------------------------------------------------------------------;
; Keyboard Key Reassignment: First convert ASCII numbers to binary, ;
; parse out delimiting semi-colons, and quote string delimiters. ;
;--------------------------------------------------------------------;
REASSIGNMENT: TEST STATUS,POFF ;:If assignment are OFF,
JZ NOT_POFF ;: then exit as if
JMP ASSIGN_FLUSH ;: we are out of room.
NOT_POFF: ;:
MOV CX,ESC_COUNT ;Retrieve length of Esc sequence.
DEC CX ;Adjust.
MOV SI,OFFSET ESC_BUFFER + 2 ;Point past Esc, bracket.
MOV DI,OFFSET PARSE_BUFFER ;Point to parse storage.
NEXT_STRING: MOV QUOTE_TYPE,0 ;Reset quote type.
NEXT_NUM: XOR DL,DL ;Use DL to carry number; init = 0
XOR BP,BP ;Use BP as number flag.
NEXT_PARAM: LODSB ;Retrieve a byte.
DEC CX ;Decrement string length counter.
JZ PARSE_END ;If zero, done.
MOV AH,QUOTE_TYPE ;Else, retrieve quote type.
OR AH,AH ;If zero, not in string.
JNZ QUOTE_MODE ;Else, go to string mode.
CALL CK_QUOTE ;Is character a quote?
JZ NEXT_PARAM ;If yes, ignore.
CMP AL,";" ;Else, is it semi-colon?
JZ STORE_NUMBER ;If yes, number delimiter.
SUB AL,"0" ;Else, must be number; convert
MOV DH,AL ; to binary; save in DH.
MOV AX,10 ;Multiply current total by ten.
MUL DL
ADD AL,DH ;Add new number.
MOV DL,AL ;Save in DL.
MOV BP,1 ;Flag that number mode active.
JMP SHORT NEXT_PARAM ;Next parameter.
STORE_NUMBER: OR BP,BP ;If number mode flag not set then
JZ NEXT_PARAM ;semi-colon not prefaced with no.
MOV AL,DL ;Else, store number.
STOSB
JMP SHORT NEXT_NUM ;Reset number carrying registers.
QUOTE_MODE: CMP AL,AH ;Is current char a string ending
JZ NEXT_STRING ; quote? If yes, exit quote mode
STOSB ;Else, store char as literal.
JMP SHORT NEXT_PARAM
;----------------------------------------------------------------------------;
; Remove duplicate assignments if removal leaves room for new assignment. ;
; If no duplicate and room, add new assignment, else flush buffer to screen. ;
;----------------------------------------------------------------------------;
PARSE_END: OR BP,BP ;Was last parameter a number?
JZ CK_LENGTH ;If no, skip.
MOV AL,DL ;Else, store the last number.
STOSB
CK_LENGTH: SUB DI,OFFSET PARSE_BUFFER - 2 ;Calculate string length
MOV AX,DI ; including word for length.
MOV BX,WORD PTR PARSE_BUFFER ;BL=new first ASCII; BH=2nd.
MOV CX,5 ;String length has to be at
OR BL,BL ;least word + 2 for old + new = 5
JZ CK_LEGAL ; for extended key reassignment.
DEC CX ;And word + 1 for old + new = 4
CK_LEGAL: CMP AX,CX ; for regular ASCII reassignment.
JB ASSIGN_FLUSH ;If not, illegal; flush buffer.
MOV BP,BUFFER_END ;BP to carry buffer end pointer.
CALL CK_MATCH ;Is char already reassigned?
JC CK_ROOM ;If no, check room for new.
CK_REMOVE: MOV CX,DX ;REASSIGN_END - string end
SUB CX,SI ; = bytes to move.
JZ CK_ROOM ;If zero, last string.
SUB DX,[DI] ;Is REASSIGN_END - old string
ADD DX,AX ; + new string >
CMP DX,BP ; BUFFER_END?
JA ASSIGN_FLUSH ;If yes, flush buffer to screen.
REP MOVSB ;Else, move them down in buffer.
JMP SHORT STORE_NEW
CK_ROOM: ADD DX,AX ;New string + current strings.
CMP DX,BP ;Greater than buffer size?
JA ASSIGN_FLUSH ;If yes, flush new string.
STORE_NEW: MOV SI,OFFSET PARSE_BUFFER ;Else, room for new string.
STOSW ;Store string length first.
MOV CX,AX ;Adjust counter.
DEC CX
DEC CX
REP MOVSB ;Store the actual string.
MOV REASSIGN_END,DI ;Store new string end.
RET
ASSIGN_FLUSH: MOV AL,"p" ;If buffer full, flush string
JMP FLUSH_BUFFER ; including ending "p" command.
;------------------------------------------------------------------------------;
; INPUT: BL = first ASCII code to match; BH = extended if BL = 0 ;
; OUTPUT: CY = 1 if no match found; CY = 0 if match found. ;
; DI points to string length of match; DI+2 points to start of string. ;
; SI = end of string; DX = REASSIGN_END; BP = BUFFER_END ;
;------------------------------------------------------------------------------;
CK_MATCH: MOV DX,REASSIGN_END ;Current strings end.
MOV SI,OFFSET REASSIGNMENT_BUFFER ;Point to buffer.
NEXT_MATCH: MOV DI,SI ;Current record.
CMP DI,DX ;End of strings?
JAE NO_MATCH ;If yes, no match.
MOV CX,[DI+2] ;CL=current first ASCII; CH=2nd
ADD SI,[DI] ;Point to next record.
CMP BL,CL ;First characters match?
JNZ NEXT_MATCH ;If no, check next record.
OR BL,BL ;Extended code? ie zero.
JNZ MATCH ;If no, then match.
CMP BH,CH ;Else, check second code.
JNZ NEXT_MATCH ;If not same, no match.
MATCH: CLC ;Else return with CY = 0
RET
NO_MATCH: STC ;No match; CY = 1.
RET
;----------------------------------------------------------------------;
; Buffer area will write over disposable data and initialization code. ;
;----------------------------------------------------------------------;
ESC_BUFFER = $
NUMBER_BUFFER = ESC_BUFFER + ESC_BUFFER_SIZE
PARSE_BUFFER = NUMBER_BUFFER
DEVICE_STATUS_BUFFER = PARSE_BUFFER + ESC_BUFFER_SIZE
DEVICE_STATUS_SIZE = 11
REASSIGNMENT_BUFFER = DEVICE_STATUS_BUFFER + DEVICE_STATUS_SIZE
; DISPOSABLE DATA
; ---------------
SYNTAX LABEL BYTE
DB "Syntax: ANSI [FAST | SLOW][ON | OFF][KON | KOFF][PON | POFF]",CR,LF ;:
DB " [/B nnn][/C][/Q][/U][/T][/S][/P n]",CR,LF ;. ;@ ;:
DB "FAST = direct screen writes; default",CR,LF ;:
DB "SLOW = screen writes via BIOS",CR,LF ;:
DB "ON/OFF = active/inactive; default is ON",CR,LF ;:
DB "KON/KOFF = active/inactive reassignments; default is ON",CR,LF ;:
DB "PON/POFF = active/inactive NEW reassignments; default is ON",CR,LF ;:
DB "nnn = buffer size in bytes (0 - 60K) reserved for key reassignment; "
DB "default 200",CR,LF
DB "/Q = Quiet, no output when executed",CR,LF ;:
DB "/U = Uninstall",CR,LF ;:
DB "/T = Test if loaded",CR,LF ;$
if PCB ;$
DB "/S = Load stats from ANSICOM.SYS",CR,LF ;@
endif ;$
DB "/Pn = Protect n lines from scrolling at top" ;.
CR_LF DB CR,LF,LF,"$"
STATUS_MSG DB "Status: $"
BUFFER_MSG DB CR,LF,"Buffer size: $"
BYTES_FREE DB CR,LF,"Bytes free: $"
ANSI_SYS_MSG DB "ANSI.SYS is installed so "
NOT_INSTALLED DB "ANSI.COM not installed",CR,LF,"$"
CON DB "CON"
CON_OFFSET EQU 10
QUIET DB 0 ;| Quiet Mode
SYNTAX_FLAG DB 0 ;; Syntax Display Flag
SIZE_MSG DB "Uninstall to change buffer size",CR,LF,LF,"$"
UNLOAD_MSG DB "ANSI can't be uninstalled",CR,LF
DB "Uninstall resident programs in reverse order",CR,LF,"$"
NOT_ENOUGH DB "Not enough memory",CR,LF,"$"
ALLOCATE_MSG DB "Memory allocation error",CR,LF,BELL,"$"
INSTALL_MSG DB "Installed",CR,LF,"$"
UNINSTALL_MSG DB "Uninstalled",CR,LF,"$"
if PCB ;$
NO_FILE_MSG DB "ANSICOM.SYS not found",CR,LF,"$" ;@
FILE_ERROR_MSG DB "Error reading ANSICOM.SYS",CR,LF,"$" ;@
FILE_NAME DB "ANSICOM.SYS",0 ;@
endif ;$
;--------------------------------------------------------------------;
; Search memory for a copy of our code, to see if already installed. ;
;--------------------------------------------------------------------;
INITIALIZE PROC NEAR
CLD ;All string operations forward.
MOV BX,OFFSET START ;Point to start of code.
NOT BYTE PTR [BX] ;Change a byte so no false match.
XOR DX,DX ;Start at segment zero.
MOV AX,CS ;Store our segment in AX.
NEXT_PARAG: INC DX ;Next paragraph.
MOV ES,DX
CMP DX,AX ;Is it our segment?
JZ ANNOUNCE ;If yes, search is done.
MOV SI,BX ;Else, point to our signature.
MOV DI,BX ; and offset of possible match.
MOV CX,16 ;Check 16 bytes for match.
REP CMPSB
JNZ NEXT_PARAG ;If no match, keep looking.
;------------------------------------------------;
ANNOUNCE: ;|
MOV SI,81H ;Point to command line.
NEXT_CAP: LODSB ;Capitalize parameters.
CMP AL,CR
JZ PARSE
CMP AL,"a"
JB NEXT_CAP
CMP AL,"z"
JA NEXT_CAP
AND BYTE PTR [SI - 1],5FH
JMP SHORT NEXT_CAP
;------------------------------------------------;
PARSE: MOV SI,81H ;Point to command line again.
NEXT_PARA: XOR AX,AX ;Position in status parameters.
MOV BX,4 ;Status parameters each 4 bytes.
NEXT_STATUS: MOV DI,OFFSET PARAMETERS ;Point to "OFF ON SLOWFAST"
ADD DI,AX ;Point to next parameter.
ADD AX,BX
CMP AX,LAST_PARAMETER ;:Check all 8 possible statuses
JA CK_SWITCHES
PUSH SI ;Save command line pointer.
MOV CX,2 ;Check first two bytes for match.
CMP AX,20 ;: 3 Bytes for
JB Len_OK ;: KON | KOFF | PON | POFF
INC CX ;:
Len_OK: ;:
REP CMPSB
POP SI ;Recover command line pointer.
JNZ NEXT_STATUS
DIV BL ;If match, divide offset by four.
;:
;: Next 12 lines of routine changed
;:
;: MOV CL,1 ;Set a bit to match offset.
;:NEXT_SHIFT: SHL CL,1
;: DEC AL
;: JNZ NEXT_SHIFT
;: SHR CL,1 ;Adjust.
;: MOV AH,STATUS_MASK ;Retrieve appropriate ON/OFF
;: CMP CL,ON ; or FAST/SLOW mask.
;: JBE ADD_STATUS
;: ROL AH,1
;: ROL AH,1
;:
;:ADD_STATUS: AND ES:STATUS,AH ;Mask off old status.
;: OR ES:STATUS,CL ;Add new status.
;:
MOV CL,AL ;:place count in CL (1 - 8)
MOV AX,STATUS_MASK ;:Retrieve appropriate ON/OFF
;:FAST|SLOW|KON|KOFF|PON|POFF
DEC CL ;:Make 1 - 8 into 0 - 7
JZ Skip_1 ;:In position already if zero
SHL AL,CL ;:Shift bit into position
Skip_1: SHR CL,1 ;:divide count by 2
JZ Skip_2 ;:
ROL AH,CL ;:Shift Mask into position
ROL AH,CL ;:
Skip_2: ;:
AND ES:STATUS,AH ;Mask off old status.
OR ES:STATUS,AL ;:Add new status.
INC SI ;Bump command line pointer.
INC SI
CK_SWITCHES: LODSB ;Get a byte.
CMP AL,CR ;Is it carriage return?
JNE QUES ;$
JMP INSTALL ;If yes, done here.
QUES: CMP AL,"?" ;;Request for Syntax?
JNE CK_SW ;; No -
MOV BYTE PTR SYNTAX_FLAG, 1 ;; Yes-Show Syntax
CK_SW: CMP AL,"/" ;;Is there a switch character?
JNE NEXT_PARA ;;If no, keep looking.
GOT_SWITCH: LODSB ;;Else, get the switch character.
CMP AL,"B" ;Is it "B" ?
JNZ CK_C ;If no, check "C".
CALL CK_INSTALLED ;Else, see if already installed.
JZ GET_SIZE ;If no, get buffer request size.
MOV DX,OFFSET SIZE_MSG ;Else, display error message.
CALL PRINT_STRING
NEXT_PARA2: JMP SHORT NEXT_PARA
GET_SIZE: CALL DECIMAL_INPUT ;Get number parameter.
CMP BX,REASSIGNMENT_MAX ;If greater than maximum, use
JBE STORE_BUFFER ; maximum, else use requested
MOV BX,REASSIGNMENT_MAX ; size.
STORE_BUFFER: MOV REASSIGNMENT_SIZE,BX
ADD BX,OFFSET REASSIGNMENT_BUFFER ;Calculate end of buffer.
MOV BUFFER_END,BX
JMP NEXT_PARA ;;Too far for direct jump
CK_C: CMP AL,"C" ;Is it "C" ?
if PCB ;$
JNZ CK_S ;@ If not, check "S".
else ;$
JNZ CK_P ;$ If not, check "P".
endif ;$
MOV ES:REASSIGN_END,OFFSET REASSIGNMENT_BUFFER ;Clear buffer
JMP NEXT_PARA ;Else, next parmater.
if PCB ;$
CK_S: CMP AL,"S" ;@ Is It "S" ?
JNZ CK_P ;. If not, check "P".
CALL LOAD_STATS ;@ Yes, Load Stats
JMP NEXT_PARA2 ;@ Next parameter (indirect)
endif ;$
CK_P: CMP AL,"P" ;. Is it "P" ?
JNZ CK_Q ;|If not, check "Q".
CMP BYTE PTR [SI], '*' ;$Use current row-1?
JNE CK_PN ;$no
PUSH SI ;$Get
CALL GET_BIOS_DATA ;$ current
MOV BL, ES:CURSOR_ROW ;$ row
DEC BL ;$ -1
POP SI ;$
JMP CK_PS ;$
CK_PN: CALL DECIMAL_INPUT ;. Get number of lines
CK_PS: MOV ES:START_SCROLL,BL ;. Store the value
JMP NEXT_PARA2 ;. Next parameter (indirect)
CK_Q: CMP AL,"Q" ;|See if Quiet Mode
JNE CK_U ;|If not, check "U".
NOT QUIET ;|If yes turn on Quiet
JMP NEXT_PARA ;Else, next parmater.
CK_U: CMP AL,"U" ;Is it "U" ?
JE DO_U ;If yes, try to uninstall.
CMP AL,"T" ;; Test if Installed
JE DO_U
MOV BYTE PTR SYNTAX_FLAG, 1 ;; Syntax Error
JMP NEXT_PARA ;Else, next parmater.
;------------------------------------------------;
INSTALL:
MOV DX,OFFSET SIGNATURE ;|Display our signature.
CALL PRINT_STRING ;|
CMP BYTE PTR SYNTAX_FLAG,0 ;;Display Syntax?
JE SKIP_SYNTAX ;;No
MOV DX,OFFSET SYNTAX ;|Yes, Display syntax.
CALL PRINT_STRING ;|
SKIP_SYNTAX: CALL STATUS_REPORT ;;|Display status.
CALL CK_INSTALLED ;Check if already installed.
JZ CK_AVAILABLE ;If no, see if enough memory.
XOR AL,AL ;;Else, done.
EXIT2: JMP EXIT ;Exit with ERRORLEVEL = 0.
DO_U: CALL CK_INSTALLED ;Else, see if installed.
MOV DX,OFFSET NOT_INSTALLED ;If no, exit with error message.
JZ LILLY_PAD ;Too far for short jump.
CMP AL,"T" ;;Was it a test
MOV AL, STATUS ;$return switches as
JE EXIT2 ;;$Yes Too far for short jump.
JMP UNINSTALL ;$Else, uninstall.
;--------------------------------;
; This is the install procedure. ;
;--------------------------------;
CK_AVAILABLE: MOV BP,OFFSET REASSIGNMENT_BUFFER ;TSR ends at end
ADD BP,REASSIGNMENT_SIZE ; of reassignment buffer.
ADD BP,15 ;Round up.
CMP BP,DS:[6] ;Buffer > PSP bytes in segment?
MOV DX,OFFSET NOT_ENOUGH ;If yes, exit without installing
JBE CK_ANSI ;@
JMP MSG_EXIT ;@with message
CK_ANSI: MOV AX,3529H ;@Get undocumented INT 29 vector.
INT 21H
MOV SI,OFFSET CON ;Does it point to ANSI.SYS?
MOV DI,CON_OFFSET ;Check by looking for "CON"
MOV CX,3 ; as device name.
REP CMPSB
MOV DX,OFFSET ANSI_SYS_MSG ;Exit with error message if
LILLY_PAD: JZ MSG_EXIT ;ANSI.SYS loaded.
MOV OLD_INT_29[0],BX ;Else save old interrupt.
MOV OLD_INT_29[2],ES
MOV DX,OFFSET ANSI_INT_29 ;Install new interrupt.
MOV AX,2529H
INT 21H
MOV AX,3516H ;Get INT 16 vector.
INT 21H
MOV OLD_INT_16[0],BX ;Save old interrupt.
MOV OLD_INT_16[2],ES
MOV DX,OFFSET ANSI_INT_16 ;Install new interrupt.
MOV AX,2516H
INT 21H
MOV AX,3521H ;Get DOS 21h interrupt.
INT 21H
MOV OLD_INT_21[0],BX ;Save old interrupt.
MOV OLD_INT_21[2],ES
MOV DX,OFFSET ANSI_INT_21 ;Install new interrupt.
MOV AX,2521H
INT 21H
if PCB ;$
MOV AX,3508H ;@ Get INT 08 vector.
INT 21H ;@
MOV OLD_INT_08[0],BX ;@ Save old interrupt.
MOV OLD_INT_08[2],ES ;@
MOV DX,OFFSET ANSI_INT_08 ;@ Install new interrupt.
MOV AX,2508H ;@
INT 21H ;@
endif ;$
MOV AX,DS:[2CH] ;Get environment segment.
MOV ES,AX
MOV AH,49H ;Free up environment.
INT 21H
MOV DX,OFFSET INSTALL_MSG ;Display install message.
CALL PRINT_STRING
CALL FLUSH_END ;Setup the number buffer.
MOV DX,BP ;Retrieve resident byte request.
MOV CL,4
SHR DX,CL ;Convert to paragraphs.
MOV AX,3100H ;Return error code of zero.
INT 21H ;Terminate but stay resident.
;-------------------------------------------------------------------;
; Exit. Return ERRORLEVEL code 0 if successful, 1 if unsuccessful. ;
;-------------------------------------------------------------------;
MSG_EXIT: CALL PRINT_STRING
ERROR_EXIT: MOV AL,1 ;ERRORLEVEL = 1.
EXIT: MOV AH,4CH ;Terminate.
INT 21H
;---------------------------------------------------;
; This subroutine uninstalls the resident ANSI.COM. ;
;---------------------------------------------------;
UNINSTALL: AND ES:STATUS,NOT ON ;Turn OFF ANSI, just in case
OR ES:STATUS,OFF ; can't uninstall.
MOV CX,ES ;Save segment in CX.
MOV AX,3529H ;Get interrupt 29h.
INT 21H
CMP BX,OFFSET ANSI_INT_29 ;Has it been hooked by another?
ife PCB ;$
JNZ UNINSTALL_ERR ;$If no, exit with error message.
else ;$
JNZ UNINSTALL_ERR2 ;@If no, exit with error message.
endif ;$
MOV BX,ES
CMP BX,CX ;Is the segment vector same?
ife PCB ;$
JNZ UNINSTALL_ERR ;$If no, exit with error message.
else ;$
JNZ UNINSTALL_ERR2 ;@If no, exit with error message.
MOV AX,3508H ;@Get interrupt 08h.
INT 21H ;@
CMP BX,OFFSET ANSI_INT_08 ;@Has it been hooked by another?
JNZ UNINSTALL_ERR ;@If yes, exit with error message.
MOV BX,ES ;@
CMP BX,CX ;@Is the segment vector same?
UNINSTALL_ERR2:JNZ UNINSTALL_ERR ;@If no, exit with error message.
endif ;$
MOV AX,3516H ;Get interrupt 16h.
INT 21H
CMP BX,OFFSET ANSI_INT_16 ;Has it been hooked by another?
JNZ UNINSTALL_ERR ;If yes, exit with error message.
MOV BX,ES
CMP BX,CX ;Is the segment vector same?
JNZ UNINSTALL_ERR ;If no, exit with error message.
MOV AX,3521H ;Get interrupt 21h.
INT 21H
CMP BX,OFFSET ANSI_INT_21 ;Has it been hooked by another?
JNZ UNINSTALL_ERR ;If yes, exit with error message.
MOV BX,ES
CMP BX,CX ;Is the segment vector same?
JNZ UNINSTALL_ERR ;If no, exit with error message.
MOV AH,49H ;Return memory to system pool.
INT 21H
MOV DX,OFFSET ALLOCATE_MSG
JC MSG_EXIT ;Display message if problem.
MOV DX,ES:OLD_INT_29[0] ;Restore old INT 29.
MOV DS,ES:OLD_INT_29[2]
MOV AX,2529H
INT 21H
if PCB ;$
MOV DX,ES:OLD_INT_08[0] ;@Restore old INT 08.
MOV DS,ES:OLD_INT_08[2] ;@
MOV AX,2508H ;@
INT 21H ;@
endif ;$
MOV DX,ES:OLD_INT_16[0] ;Restore old INT 16.
MOV DS,ES:OLD_INT_16[2]
MOV AX,2516H
INT 21H
MOV DX,ES:OLD_INT_21[0] ;Restore old INT 21.
MOV DS,ES:OLD_INT_21[2]
MOV AX,2521H
INT 21H
PUSH CS
POP DS ;Point to our data.
MOV DX,OFFSET UNINSTALL_MSG ;Display uninstall message.
CALL PRINT_STRING
OR AL,AL ;Exit with ERRORLEVEL = 0.
JMP EXIT
UNINSTALL_ERR: MOV ES,CX ;If error, display OFF status.
CALL STATUS_REPORT
MOV DX,OFFSET UNLOAD_MSG ;And exit with error message.
JMP MSG_EXIT
INITIALIZE ENDP
;--------------------------------------------------;
; INPUT: SI points to parameter start. ;
; OUTPUT: SI points to parameter end; BX = number. ;
;--------------------------------------------------;
DECIMAL_INPUT PROC NEAR
XOR BX,BX ;Start with zero as number.
NEXT_DECIMAL: LODSB ;Get a character.
CMP AL,CR ;Carriage return?
JZ ADJUST_DEC ;If yes, done here.
CMP AL,"/" ;Forward slash?
JZ ADJUST_DEC ;If yes, done here.
SUB AL,"0" ;ASCII to binary.
JC NEXT_DECIMAL ;If not between 0 and 9, skip.
CMP AL,9
JA NEXT_DECIMAL
CBW ;Convert byte to word.
XCHG AX,BX ;Swap old and new number.
MOV CX,10 ;Shift to left by multiplying
MUL CX ; last entry by ten.
JC DECIMAL_ERROR ;If carry, too big.
ADD BX,AX ;Add new number and store in BX.
JNC NEXT_DECIMAL ;If not carry, next number.
DECIMAL_ERROR: MOV BX,-1 ;Else, too big; return -1.
ADJUST_DEC: DEC SI ;Adjust pointer.
RET
DECIMAL_INPUT ENDP
;-------------------------------------------------------;
; OUTPUT: ZR = 1 if not installed; ZR = 0 if installed. ;
;-------------------------------------------------------;
CK_INSTALLED: PUSH AX ;;
MOV AX,ES
MOV BX,CS
CMP AX,BX ;Compare segments.
POP AX ;;
RET
;------------------------------------------------;
STATUS_REPORT: MOV DX,OFFSET STATUS_MSG
CALL PRINT_STRING ;Display "Status: ".
MOV BL,ES:STATUS
MOV BH,1
NEXT_REPORT: TEST BL,BH
JZ LOOP_STATUS
MOV DL,BH
;:
;: Next 9 Lines changed
;:
;: MOV AX,-1
;:NEXT_BIT: INC AX
;: SHR DL,1
;: JNC NEXT_BIT
;: SHL AX,1
;: SHL AX,1
;: MOV SI,OFFSET PARAMETERS ;Display appropriate ON or OFF,
;: ADD SI,AX ; SLOW or FAST.
;: MOV CX,4
;:
MOV SI,OFFSET PARAMETERS ;:Display appropriate ON or OFF,
;: SLOW|FAST. KON|KOFF|PON|POFF
MOV CX,4 ;:Each four characters long
NEXT_BIT: SHR DL,1 ;:Is this our Word?
JC REPORT ;:Yes - Print it
ADD SI,CX ;:No - Skip over it
JMP NEXT_BIT ;:test the next bit
REPORT: LODSB
CALL PRINT_CHAR
LOOP REPORT
MOV AL, " " ;: Space words
CALL PRINT_CHAR ;:
LOOP_STATUS: SHL BH,1
JNC NEXT_REPORT ;: Check all 8 bits
;:
MOV DX,OFFSET BUFFER_MSG ;Display "Buffer size: ".
CALL PRINT_STRING
MOV AX,ES:REASSIGNMENT_SIZE
PUSH AX
XOR BP,BP
CALL DECIMAL_OUT ;Display the size.
MOV DX,OFFSET BYTES_FREE ;Display "Bytes free: ".
CALL PRINT_STRING
POP AX
ADD AX,OFFSET REASSIGNMENT_BUFFER
SUB AX,ES:REASSIGN_END
CALL DECIMAL_OUT ;Display the bytes free.
MOV DX,OFFSET CR_LF
CALL PRINT_STRING
RET
;------------------------------------------------;
PRINT_CHAR: MOV DL,AL
MOV AH,2 ;Print character via DOS.
OR AL,AL ;:Skip NULLs
JNZ SHORT DOS_INT ;:
RET ;:
PRINT_STRING: MOV AH,9 ;Print string via DOS.
DOS_INT: CMP QUIET,0 ;|Do we suppress output
JNE NO_PRINT ;|Yes
INT 21H ;|No
NO_PRINT: ;|
RET
if PCB ;$
LOAD_STATS: MOV DX,OFFSET FILE_NAME ;@ Stat filename
MOV AX,3D00H ;@ Open File
INT 21H ;@ DOS Interupt
MOV DX,OFFSET NO_FILE_MSG ;@ Assume no file
JNC OPEN_OK ;@ Ok, So far
MSG_EXIT2: JMP MSG_EXIT ;@ No file found probably
OPEN_OK: MOV BX,AX ;@ This is our Handle number
PUSH DS ;@ Save DS
PUSH ES ;@ Set ES to DS
POP DS ;@
MOV DX, OFFSET SUB_LIST ;@ Point to the Sub_List
MOV CX, SUB_LENGTH ;@ Get the Length ot the List
MOV AH,3FH ;@ Read from Handle
INT 21H ;@ DOS Interupt
POP DS ;@ Restore DS
PUSHF ;@ Save results
PUSH AX ;@
MOV AH,3EH ;@ Close Handle
INT 21H ;@ DOS Interupt
POP AX ;@ Restore results
POPF ;@
MOV DX,OFFSET FILE_ERROR_MSG ;@ Assume an error occured
JC MSG_EXIT2 ;@ Yep, it did
XCHG AX,CX ;@ Swap Bytes Read
JCXZ MSG_EXIT2 ;@ Something went wrong
CMP AX,CX ;@ Did we read the full amount
JNE MSG_EXIT2 ;@ No
RET ;@ All went well!
endif ;$
_TEXT ENDS
END START